//-----------------------------------------------------------------------------
//
// @file	BezierCurve.h
// @brief	BezierCurve include files, variables and classes
// @author	Vinod Melapudi
// @date	20Feb2010
//
//-----------------------------------------------------------------------------

#ifndef _BEZIERCURVE_H
#define _BEZIERCURVE_H

#include "math.h"

#include <vector>

//-----------------------------------------------------------------------------

// 3rd degree Bezier curve given 4 control points (blending)
template<class T>
void BezierCurve( T pt1, T pt2, T pt3, T pt4, int numSegments, std::vector<T>& curvePoints )
{
	// B(t) = (1-t)^3 * P1 + 3t(1-t)^2 * P2 + 3t^2(1-t) * P3 + t^3 * P4

	float dt = 1.f / numSegments;
	float t = 0.f;
	for( int i=0; i <= numSegments; i++ )
	{
		T cpt = (1-t)*(1-t)*(1-t) * pt1 +
				3*t*(1-t)*(1-t) * pt2 +
				3*t*t*(1-t) * pt3 +
				t*t*t * pt4;
		curvePoints.push_back( cpt );
		t += dt;
	}
}

//---------------------------------------------------------------------------

// Bezier curve that runs through given points (piece-wise)
template<class T>
void BezierCurve( std::vector<T>& keyPoints, int numSegments, std::vector<T>& curvePoints )
{
	std::vector<T> dividingPoints;
	std::vector<T> tangentPoints;

	if ( keyPoints.size() < 3 )
		return;

	curvePoints.clear();

	// Find the dividing vector for adjacent key points and get the tangent points
	tangentPoints.push_back(  keyPoints[0] + (keyPoints[1] - keyPoints[0])/2.f  );
	
	T dv, tv, kl1, kl2;
	for( size_t i=1; i < keyPoints.size()-1; i++ )
	{
		kl1 = (keyPoints[i-1] - keyPoints[i]).normalize();
		kl2 = (keyPoints[i+1] - keyPoints[i]).normalize();

		// Choose tangent length
		float tl1 = (keyPoints[i-1] - keyPoints[i]).norm();
		float tl2 = (keyPoints[i+1] - keyPoints[i]).norm();
		float tl = ( tl1 < tl2 ? tl1 : tl2 ) / 2.0f;


		dv = kl1 + kl2;
		dividingPoints.push_back( keyPoints[i]+dv*tl );


		tv.x = dv.x * cos(PI/2) - dv.y * sin(PI/2);
		tv.y = dv.x * sin(PI/2) + dv.y * cos(PI/2);
		tv = tv.normalize() * tl;

		// Choose tangent side
		if ( tv * kl1 > tv * kl2)
		{
			tangentPoints.push_back( keyPoints[i]+tv );
			tangentPoints.push_back( keyPoints[i]-tv );
		}
		else
		{
			tangentPoints.push_back( keyPoints[i]-tv );
			tangentPoints.push_back( keyPoints[i]+tv );
		}
	}

	int i = keyPoints.size()-2;
	tangentPoints.push_back(  keyPoints[i] + (keyPoints[i+1] - keyPoints[i])/2.f  );

	// Calculate curve points
	for( size_t i=0; i < keyPoints.size()-1; i++ )
	{
		BezierCurve<T>( keyPoints[i], tangentPoints[2*i], tangentPoints[2*i+1], keyPoints[i+1], numSegments, curvePoints );

		// Remove last redundant point
		if ( i < keyPoints.size()-2 )
			curvePoints.pop_back();
	}
}

#endif